#include <p33Fxxxx.h>
#include <string.h>
#include "zerdlib.h"
#include "algos/lists/ByteFIFO.h"
#include "UART.h"

ByteFIFO U2RX_buffer, U2TX_buffer;
U16 U2RX_almostFullAlert, U2TX_almostFullAlert;
U8 U2_intProtect;

UART_ERROR UART2_init(U8 txPin, U8 rxPin, U32 baudrate, U16 txBufferSize, U16 rxBufferSize, U8 txIntPriority, U8 rxIntPriority, U8 intProtect) {
    // Initialisation des buffers

    U2TX_almostFullAlert = txBufferSize*9/10;
    U2TX_buffer = ByteFIFO_new(txBufferSize);
    if (U2TX_buffer == null) {
        return UART_ERROR_OUTOFMEMORY;
    }

    U2RX_almostFullAlert = rxBufferSize*9/10;
    if(rxBufferSize>0) {
        U2RX_buffer = ByteFIFO_new(rxBufferSize);
        if (U2RX_buffer == null) {
            ByteFIFO_free(U2TX_buffer);
            return UART_ERROR_OUTOFMEMORY;
        }
    } else {
        U2RX_buffer=null;
    }

    // Mappage des ports
    PPSInput(U2RX, rxPin);
    PPSOutput(U2TX, txPin);

    // Réglage de la vitesse de communication
    U2MODE = 0x0008; // mode rapide (si =0, il faut remplacer le /4 par /16) convient pour des baudrates jusqu'à plusieurs Mb
    U2BRG = ((FCY / baudrate) / 4) - 1;
    U2STA = 0;

    // Initialisation des interruptions
    if(intProtect>txIntPriority && intProtect>rxIntPriority) {
        U2_intProtect = intProtect;
    } else {
        U2_intProtect =   txIntPriority>rxIntPriority ? txIntPriority : rxIntPriority;
    }

    _U2TXIF = 0;
    _U2TXIP = txIntPriority;
    _U2TXIE = 1;

    U2STAbits.URXISEL = 0;
    _U2RXIF = 0;
    _U2RXIP = rxIntPriority;
    _U2RXIE = rxIntPriority>0?1:0;

    // Démarrage du module
    U2MODEbits.UARTEN = 1;
    U2STAbits.UTXEN = 1;

    return UART_ERROR_OK;
}

UART_ERROR UART2_send(const void * tab, U16 size) {
    U8 ret;
    INTERRUPT_PROTECT(U2_intProtect, {
        ret = ByteFIFO_pushBlock(U2TX_buffer, size, tab);
    });

    _U2TXIF = 1;
    return ret ? UART_ERROR_OK : UART_ERROR_BUFFEROVERFLOW;
}

UART_ERROR UART2_sendStr(const char* str) {
    return UART2_send(str, strlen(str));
}

UART_ERROR UART2_read(void * tab, U16 size) {
    U8 ret;
    INTERRUPT_PROTECT(U2_intProtect, {
        ret = ByteFIFO_popBlock(U2RX_buffer, size, tab);
    });

    return ret ? UART_ERROR_OK : UART_ERROR_NOTENOUGHTDATA;
}

void UART2_onU2TXInterrupt(void) {
    if (_U2TXIF) {
        U8 ret;
        INTERRUPT_PROTECT(U2_intProtect, {
            while (!U2STAbits.UTXBF && !ByteFIFO_isEmpty(U2TX_buffer)) {
                U2TXREG = ByteFIFO_pop(U2TX_buffer);
            }
        });
        _U2TXIF = 0;
    }
}

UART_ERROR UART2_onU2RXInterrupt() {
    U8 ret = UART_ERROR_OK;
    if(_U2RXIF) {
        _U2RXIF=0;
        while(U2STAbits.URXDA && ret == UART_ERROR_OK) {
            U8 value = U2RXREG;

            INTERRUPT_PROTECT(U2_intProtect, {
                if(ByteFIFO_isFull(U2RX_buffer)) {
                    ret = UART_ERROR_BUFFEROVERFLOW;
                } else {
                    ByteFIFO_push(U2RX_buffer,value);
                    if(ByteFIFO_getDataSize(U2RX_buffer)>U2RX_almostFullAlert) {
                        ret = UART_ERROR_BUFFERALMOSTFULL;
                    } else {
                        ret = UART_ERROR_OK;
                    }
                }
            });
        }
        return ret;
    }
}

